/* A region is a cluster of cities. The user can choose one of the cities of
 * the region to play. The region can have the following types:
 *
 *   - OPEN_ENDED: the cities in this region cannot be "won", the user will play
 *                 until he sees fit. This is the only type of region the user can
 *                 create without a scenario editor.
 *   - SCENARIO: each city will have specific goals. When the city meets these
 *               goals, it means the player "wins" that "level". The cities the
 *               user didn't won will be locked, except for the next one.
 *   - TUTORIAL: the same as scenario, but the levels contain instructions to
 *               teach the player how to play the game.
 *
 * The list of regions is saved in a file called regions.dat.
 *
 * The region is divided in cities following this layout:
 *
 *   +---+---+---+---+
 *   | 1 | 2 | 3 | 4 |
 *   +---+---+---+---|
 *   |   5   |   6   |
 *   +-------+-------|
 *   |       |       |
 *   |   7   |   8   |
 *   |       |       |
 *   +-------+-------+
 */

#include "region.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <time.h>
#include <sqlite3.h>

#include "global.h"
#include "data.h"


// Open a connection to the database and fetch the region name from the DB.
static int get_region_name(char* db_file, char** name)
{
	sqlite3 *db_r;
	int ret;

	int error = sqlite3_open(configpath(db_file), &db_r);
	if(error != SQLITE_OK)
		return 0;

	sqlite3_stmt* stmt;
	sqlite3_prepare_v2(db_r, "select name from region;", -1, &stmt, NULL);
	if(sqlite3_step(stmt) == SQLITE_ROW)
	{
		*name = strdup((char*)sqlite3_column_text(stmt, 0));
		ret = -1;
	}
	else
		ret = 0;
	sqlite3_finalize(stmt);

	return ret;
}


// Return the list of the regions names for that object. The names need to be
// freed after used.
int regions_names(RegionType type, char** names)
{
	// TODO - get only the specified type

	DIR *dirp;
	struct dirent *dp;
	int cn = 0;

	if((dirp = opendir(configpath(""))) == NULL)
		ui_error(_("Could not open directory %s."), configpath(""));

	do
	{
		errno = 0;
		if((dp = readdir(dirp)) != NULL)
		{
			int sl = strlen(dp->d_name);
			if(sl > 7)
			{
				char* ext = &dp->d_name[sl-7];
				if(strcmp(".region", ext) == 0)
				{
					char *name;
					if(get_region_name(dp->d_name, &name))
						names[cn++] = name;
				}
			}
		}
    	} while (dp != NULL);

	closedir(dirp);

	return cn;
}


// Create a new region database.
static int create_database(char* db_file)
{
	// create region database
	int error = sqlite3_open(db_file, &db);
	if(error != SQLITE_OK)
		return error;

	// create tables
	sq3_test(sqlite3_exec(db,
		"create table region ( name text primary key"
		"                    , type number(1)"
		"		     , w number(4)"
		"		     , h number(4)"
		"		     , data blob );", NULL, NULL, NULL));
	sq3_test(sqlite3_exec(db,
		"create table cities ( code number(1) primary key "
		"                    , name text unique"
		"		     , w number(4)"
		"		     , h number(4) );", NULL, NULL, NULL));
	sq3_test(sqlite3_exec(db,
		"create table tiles ( city number(1) references cities (code) on delete cascade "
		"                   , x number(4)"
		"                   , y number(4)"
		"                   , n_trees number(1)"
		"                   , terrain number(1)"
		"                   , structure number(5)"
		"     , primary key ( city, x, y ) );", NULL, NULL, NULL));

	return SQLITE_OK;
}


// create a river in the region
static void make_river(Region* r)
{
	// the river can flow from the west, or from the north
	enum { WEST, NORTH } side = WEST; //(rand() % 2 == 0) ? WEST : NORTH;

	// starting position
	int width = 15 + (rand() % 10);
	int x, y;
	if(side == WEST) 
	{
		x = 0;
		y = rand() % r->h;
	} 
	else 
	{
		x = rand() % r->w;
		y = 0;
	}

	// draw river
	while((side == WEST) ? (x < r->w) : (y < r->h))
	{
		// draw water
		int i;
		for(i=-(width/2); i<(width/2); i++)
		{
			if(side == WEST)
			{
				if(y+i >= 0 && y+i < r->h)
					r->data[x + ((y+i) * r->w)] = WATER;
			}
			else
			{
				if(x+i >= 0 && x+i < r->h)
					r->data[(x+i) + (y * r->w)] = WATER;
			}
		}

		// change direction
		int r = rand() % 5;
		if(r == 0)
			(side == WEST) ? ++y : ++x;
		else if(r == 1)
			(side == WEST) ? --y : --x;
		r = rand() % 5;
		if(r == 0)
			--width;
		else if(r == 1)
			++width;

		(side == WEST) ? ++x : ++y;
	}
}


// create the sea in the region
static void make_sea(Region* r)
{
	// define the side of the region that'll contain the sea
	enum { WEST, EAST } side = (rand() % 2 == 0) ? WEST : EAST;
	
	int y, s = 25;
	for(y=0; y<r->h; y++)
	{
		int x = 0;
		for(x = 0; x <= s; x++)
		{
			int px = (side == WEST) 
				? x
				: (r->w - 1 - x);                 
			r->data[px + (y * r->w)] = WATER;
			
		}

		int rd = rand() % 4;
		if(rd == 2)
			s++;
		else if(rd == 3)
			s--;
	}
}


// Create a new region, with disk persistence.
Region* new_region(char* name)
{
	int i;

	// create region seed
	srand(time(NULL));

	// create region object
	Region* r = malloc(sizeof(Region));
	r->name = strdup(name);
	r->type = OPEN_ENDED;
	r->w = 360;
	r->h = 360;
	for(i=0; i<8; i++)
		r->city_names[i] = NULL;

	// draw map
	r->data = malloc(sizeof(char) * r->w * r->h);
	for(i=0; i<(r->w * r->h); i++)
		r->data[i] = (rand() % 5 == 0) ? TREES : LAND;
	make_sea(r);
	make_river(r);

	// get region filename
	char fn[512];
	sprintf(fn, "%s.region", convert_to_filename(r->name));
	r->filename = strdup(configpath(fn));

	// open/create database
	int error = sqlite3_open_v2(configpath(fn), &db,
			SQLITE_OPEN_READWRITE, NULL);
db_again:
	if(error != SQLITE_OK)
	{
		if(error == SQLITE_CANTOPEN) // db not found, let's create
		{
			error = create_database(configpath(fn));
			if(error == SQLITE_CANTOPEN)
				abort();
			goto db_again;
		}
		else // a real errro
			sq3_error();
	}

	// store information in the database
	char sql[1024];
	sprintf(sql, "insert or replace into region (name, type, w, h, data) "
		     "values ( '%s', %d, %d, %d, ? );", r->name, r->type,
		     r->w, r->h);
	sqlite3_stmt* stmt;
	sq3_test(sqlite3_prepare_v2(db, sql, -1, &stmt, NULL));
	sq3_test(sqlite3_bind_blob(stmt, 1, r->data, (r->w * r->h), NULL));
	sq3_test(sqlite3_step(stmt));
	sq3_test(sqlite3_finalize(stmt));

	return r;
}


// Load a region from the database file.
Region* load_region(char* name)
{
	int i;

	// open database
	char filename[1024];
	sprintf(filename, "%s.region", configpath(convert_to_filename(name)));
	if(db == NULL)
		sq3_test(sqlite3_open_v2(filename, &db, SQLITE_OPEN_READWRITE, NULL));

	// fetch data from DB and create the region object
	Region* r = malloc(sizeof(Region));
	sqlite3_stmt* stmt;
	sq3_test(sqlite3_prepare_v2(db, 
				"select name, type, w, h, data from region", 
				-1, &stmt, NULL));
	sq3_test(sqlite3_step(stmt));
	r->name = strdup((char*)sqlite3_column_text(stmt, 0));
	r->type = sqlite3_column_int(stmt, 1);
	r->w = sqlite3_column_int(stmt, 2);
	r->h = sqlite3_column_int(stmt, 3);
	r->data = malloc(sqlite3_column_bytes(stmt, 4));
	r->filename = strdup(filename);
	memcpy(r->data, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
	sq3_test(sqlite3_finalize(stmt));

	// fetch city names from DB
	for(i=0; i<8; i++)
		r->city_names[i] = NULL;
	sq3_test(sqlite3_prepare_v2(db, "select code, name from cities", 
				-1, &stmt, NULL));
	int status;
	while((status = sqlite3_step(stmt)) == SQLITE_ROW)
	{
		int n = sqlite3_column_int(stmt, 0);
		assert(n >= 1 && n <= 8);
		r->city_names[n-1] = strdup((const char*)sqlite3_column_text(stmt, 1));
	}
	if(status != SQLITE_DONE)
		sq3_test(status);
	sq3_test(sqlite3_finalize(stmt));


	// finalize

	return r;
}


// Recreate the region.
Region* region_reboot(Region* r)
{
	char* name = strdup(r->name);
	region_delete(r);
	Region* nr = new_region(name);
	free(name);
	return nr;
}


void region_free(Region* r)
{
	sq3_test(sqlite3_close(db));
	free(r->name);
	free(r->filename);
	free(r->data);
	free(r);
	db = NULL;
}


// Delete the region, along with the database file.
void region_delete(Region* r)
{
	// delete file
	cm_remove(r->filename);

	// free region
	region_free(r);
}


void region_rename(Region* r, char* new_name)
{
	// rename region object
	char* tmp = r->name;
	r->name = strdup(new_name);
	free(tmp);

	// update name in the database
	sqlite3_stmt* stmt;
	sq3_test(sqlite3_prepare_v2(db, "update region set name = ?", -1, &stmt, NULL));
	sq3_test(sqlite3_bind_text(stmt, 1, new_name, strlen(new_name), NULL));
	sq3_test(sqlite3_step(stmt));
	sq3_test(sqlite3_finalize(stmt));
	
	// close database
	sq3_test(sqlite3_close(db));

	// rename file
	char filename[1024];
	sprintf(filename, "%s.region", configpath(convert_to_filename(new_name)));
	cm_rename(r->filename, filename);

	// rename filename object
	tmp = r->filename;
	r->filename = strdup(filename);
	free(tmp);
	
	// reopen DB
	sq3_test(sqlite3_open_v2(filename, &db, SQLITE_OPEN_READWRITE, NULL));
}
